package tum0r.webengine.extension

import tum0r.webengine.config.BaseConfig
import tum0r.webengine.config.LogFileConfig
import tum0r.webengine.enums.EConfigType
import tum0r.webengine.enums.EHashType
import tum0r.webengine.models.core.HttpError
import java.io.File
import java.net.URLDecoder
import java.net.URLEncoder
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.*
import kotlin.system.exitProcess

/**
 * 说明: 字符串操作扩展
 *
 * 创建者: tum0r
 *
 * 时间: 2021/8/18 17:10
 */

/**
 * 抛出HTTP错误
 */
@JvmOverloads
fun String.check(flag: Boolean, code: Int = 440) {
    if (!flag) return
    this.errorLog()
    throw HttpError(code, this)
}

/**
 * 检查字符串是否为空，若是则抛出this错误信息
 *
 * @param[values] 被检查的字符串
 */
fun String.checkBlank(vararg values: String) {
    for (v in values) if (v.isBlank()) this.check(true)
}

/**
 * 检查对象是否为空，若是则抛出this错误信息
 *
 * @param[values] 被检查的对象
 */
fun String.checkNull(vararg values: Any?) {
    for (v in values) if (v == null) this.check(true)
}

/**
 * 检查字符串是否为null或空，若是则抛出this错误信息
 *
 * @param[values] 被检查的字符串
 */
fun String.checkNullOrBlank(vararg values: String?) {
    this.checkNull(*values)
    val array = Array(values.size) { "" }
    for (i in values.indices) array[i] = values[i]!!
    this.checkBlank(*array)
}

/**
 * 检查字符串为空时提示
 */
fun String.check(name: String): Boolean {
    if (this.isBlank()) {
        "${name}不能为空".errorLog()
        return false
    }
    return true
}

/**
 * 数据库 LIKE 语句添加占位符（默认%）
 *
 * [left] 是否添加左百分号
 * [right] 是否添加右百分号
 */
@JvmOverloads
fun String.fillLikeString(fillString: String = "%", left: Boolean = true, right: Boolean = true): String = (if (left) fillString else "") + this + (if (right) fillString else "")

/**
 * 对字符串进行遮挡，例如：tum0r -> t***r
 *
 * @param[start] 被遮挡的开始下标，大于等于0
 * @param[end] 被遮挡的结束下标，小于等于字符串长度，遮挡时不包括此下标
 * @param[maskCharacterLength] 遮挡字符的数量，默认为0，表示根据end和start进行计算
 * @param[maskCharacter] 遮挡字符，默认为'*'
 *
 * @return 遮挡后的字符串
 *
 * @throws[Exception] 起始位置小于0，结束位置超过字符串长度，起始位置大于等于结束位置
 */
@JvmOverloads
fun String.mask(start: Int, end: Int, maskCharacterLength: Int = 0, maskCharacter: Char = '*'): String {
    if (start < 0) throw RuntimeException("起始位置不能小于0")
    if (end > this.length) throw RuntimeException("结束位置不能超过字符串长度: " + this.length)
    if (start >= end) throw RuntimeException("起始位置不能大于或等于结束位置")

    val temp = this.toCharArray()
    val result = StringBuilder()
    for (i in 0 until start) result.append(temp[i])

    val maskCharacterString = maskCharacter.toString()
    if (maskCharacterLength == 0) result.append(maskCharacterString.repeat(end - start))
    else result.append(maskCharacterString.repeat(maskCharacterLength))

    for (i in end until this.length) result.append(temp[i])
    return result.toString()
}

/**
 * 字符串IP地址转成Int类型
 */
fun String.getIPInt(): Int {
    val temp = this.split(".")
    val result = IntArray(4)
    for (i in temp.indices) result[i] = temp[i].toInt()
    return result.getIPInt()
}

/**
 * 获取不带后缀的文件名
 */
fun String.fileNameWithoutExtension(): String = File(this).nameWithoutExtension

/**
 * 获取文件后缀名
 */
fun String.fileExtension(): String = File(this).extension

// region 文件检查
/**
 * 是否是文件（文件是否存在）
 *
 * @param[ignoreIsHidden] 是否忽略被检查的文件是不是隐藏的
 */
@JvmOverloads
fun String.isExistsFile(ignoreIsHidden: Boolean = false): Boolean = File(this).isExistsFile(ignoreIsHidden)

/**
 * 是否是文件夹（文件夹是否存在）
 *
 * @param[ignoreIsHidden] 是否忽略被检查的文件是不是隐藏的
 */
@JvmOverloads
fun String.isExistsFolder(ignoreIsHidden: Boolean = false): Boolean = File(this).isExistsFolder(ignoreIsHidden)

/**
 * 检查是否存在目录穿越
 *
 * @param[filePath] 被检查的文件路径
 */
fun String.checkDirThrough(filePath: String): Boolean = File(this).checkDirThrough(File(filePath))
// endregion

// region 操作文件/文件夹
/**
 * 创建文件夹
 */
fun String.createDir(): Boolean = File(this).createDir()

/**
 * 获取文件大小，文件夹则把其中所有文件大小加起来
 *
 * @param[ignoreIsHidden] 是否忽略是否是隐藏文件
 */
@JvmOverloads
fun String.getFileSize(ignoreIsHidden: Boolean = false): Long = File(this).getFileSize(ignoreIsHidden)
// endregion

// region 日志
/**
 * 输出日志到控制台和文件
 */
private fun String.log(type: EConfigType) {
    val message = when (type) {
        EConfigType.LOG_MESSAGE_INFO -> "[INFO] [${Date().format()}] $this"
        EConfigType.LOG_MESSAGE_DEBUG -> "[DEBUG] [${Date().format()}] $this"
        EConfigType.LOG_MESSAGE_ERROR -> "[ERROR] [${Date().format()}] $this"
        else -> TODO("输出类型错误")
    }
    if (BaseConfig.isState(type)) println(message.trim())
    val logFileType = when (type) {
        EConfigType.LOG_MESSAGE_DEBUG -> EConfigType.SAVE_DEBUG_LOG_TO_FILE
        EConfigType.LOG_MESSAGE_ERROR -> EConfigType.SAVE_ERROR_LOG_TO_FILE
        else -> EConfigType.SAVE_INFO_LOG_TO_FILE
    }
    if (!BaseConfig.isState(logFileType)) return
    LogFileConfig.logToFile(logFileType, message)
}

/**
 * 输出普通级别日志
 */
fun String.infoLog() = log(EConfigType.LOG_MESSAGE_INFO)

/**
 * 输出调试级别日志
 */
fun String.debugLog() = log(EConfigType.LOG_MESSAGE_DEBUG)

/**
 * 输出错误级别日志
 */
@JvmOverloads
fun String.errorLog(exit: Boolean = false) {
    log(EConfigType.LOG_MESSAGE_ERROR)
    if (exit) exitProcess(1)
}
// endregion

// region 加密
/**
 * 获取字符串的散列值
 *
 * @param[type] 散列值类型
 */
@JvmOverloads
fun String.hash(type: EHashType = EHashType.SHA256) = this.toByteArray().hash(type)
// endregion

// region URL
/**
 * 对字符串进行URL编码
 *
 * @param[charset] 编码格式
 */
@JvmOverloads
fun String.urlEncode(charset: Charset = StandardCharsets.UTF_8): String = URLEncoder.encode(this, charset)

/**
 * 对字符串进行URL解码
 *
 * @param[charset] 编码格式
 */
@JvmOverloads
fun String.urlDecode(charset: Charset = StandardCharsets.UTF_8): String = URLDecoder.decode(this, charset)
//endregion

/**
 * 字符串是否为null或空
 */
fun String?.isNullOrBlank() = this == null || this.isBlank()

/**
 * 字符串是否不为null也不为空
 */
fun String?.isNotNullOrBlank() = !this.isNullOrBlank()

/**
 * 字符串转Date类型
 *
 * @param[format] 日期格式
 */
@JvmOverloads
fun String.parseDate(format: String = "yyyy-MM-dd"): Date = SimpleDateFormat(format).parse(this)
